#include <sys/time.h>
#include <queue>
#include "IDA/IDA.h"
#include "A/A.h"

inline void swap(short int *array, short int i, short int j)
{
	int aux = array[i];
	array[i] = array[j];
	array[j] = aux;
}

typedef struct
{
	bool operator() (FifteenPuzzleNodeA &x, FifteenPuzzleNodeA &y) const
	{
		if (x.getH() < y.getH()) return false;

		return true;
	}
}PriorityFunc;

void printArray(char *filename, bool *X, int Nx)
{
	int i = 0;
	int board[16];
	int invBoard[16];

	priority_queue<FifteenPuzzleNodeA, vector<FifteenPuzzleNodeA>, PriorityFunc> pq;
	PDBHeuristic *PDBcalculator = new PDBHeuristic(Nx,X);

	FifteenPuzzleNodeA So;

	for(i = 0; i < 16; i++)
		invBoard[i] = i;

	So.setPosiciones(invBoard);
	So.setG(0);
	So.setH(0);

	PDBcalculator->H[PDBcalculator->getRank(invBoard)] = 0;

	pq.push(So);

	while(!pq.empty())
	{

		FifteenPuzzleNodeA S = pq.top();
		pq.pop();

		S.getBoard(board);

		for(i = 0; i < 16; i++)
			invBoard[board[i]] = i;

		int dh = S.getH();

		vector<FifteenPuzzleNodeA> succesors = S.getSuccesors(board, invBoard, PDBcalculator, dh);
		vector<FifteenPuzzleNodeA>::iterator it;

		for(it = succesors.begin(); it != succesors.end(); it++)
			pq.push(*it);

	}

	ofstream file;

	file.open(filename);

	for(i = 0;i<PDBcalculator->Hsize; i++)
		file << ((int) PDBcalculator->H[i]) << endl;

	file.close();

	delete PDBcalculator;

}

void runFifteenPuzzleNodeA(char *filename, char *heuristic)
{
	ifstream file (filename);

	FifteenPuzzleNodeA *So;// = new FifteenPuzzleNodeA();
	Manhattan15Puzzle *M15heuristic = NULL;
	PDBHeuristic *FPDB15heuristic = NULL;
	PDBHeuristic *SPDB15heuristic = NULL;
	PDBHeuristic *TPDB15heuristic = NULL;
	A AInstance;
	int heuristicN = -1, count = 0;
	double start, finish;
	struct timeval t_p;

	int board[16];

	//So->setG(0);

	if(strcmp(heuristic, "Manhattan") == 0)
	{
		heuristicN = Manhattan;
		M15heuristic = new Manhattan15Puzzle();
		AInstance.manhattanCalculator = M15heuristic;
	}
	else if(strcmp(heuristic, "PDB") == 0)
	{
		heuristicN = PDB;

		int fn = 8, sn = 8, tn = 2;

		bool *fX = new bool[16];
		bool *sX = new bool[16];
		bool *tX = new bool[16];

		fX[0] = false; fX[1] = true; fX[2] = false; fX[3] = true;
		fX[4] = true; fX[5] = false; fX[6] = true; fX[7] = false;
		fX[8] = false; fX[9] = true; fX[10] = false; fX[11] = true;
		fX[12] = true; fX[13] = false; fX[14] = true; fX[15] = false;

		char filename[3] = "f1";

		FPDB15heuristic = new PDBHeuristic(fn, fX, filename);

		AInstance.fpdb = FPDB15heuristic;

		sX[0] = false; sX[1] = false; sX[2] = true; sX[3] = false;
		sX[4] = false; sX[5] = true; sX[6] = false; sX[7] = true;
		sX[8] = true; sX[9] = false; sX[10] = true; sX[11] = true;
		sX[12] = false; sX[13] = true; sX[14] = false; sX[15] = true;

		filename[1] = '2';

		SPDB15heuristic = new PDBHeuristic(sn, sX, filename);

		AInstance.spdb = SPDB15heuristic;

		tX[0] = false; tX[1] = true; tX[2] = true; tX[3] = true;
		tX[4] = true; tX[5] = true; tX[6] = true; tX[7] = true;
		tX[8] = true; tX[9] = true; tX[10] = true; tX[11] = false;
		tX[12] = true; tX[13] = true; tX[14] = true; tX[15] = true;

		filename[1] = '3';

		TPDB15heuristic = new PDBHeuristic(tn, tX, filename);

		AInstance.tpdb = TPDB15heuristic;


	}
	else
	{
		cout << "Heuristica invalida." << endl;
		exit(-1);
	}

	AInstance.heuristic  = heuristicN;

	if (file.is_open())
	{
		while ( file.good())
		{
			cout << "Instancia " << count  << ": ";

			for(int i = 0; i < 16; i++)
			{

				file >> board[i];

				if(file.eof())
				{
					file.close();
					exit(0);
				}

				cout << board[i] << " ";
			}

			cout << " | ";

			count++;

			So = new FifteenPuzzleNodeA();
			So->setPosiciones(board);

			if(heuristicN == Manhattan)
				So->setH(M15heuristic->initiate(board));
			else
			{
				So->getInvBoard(board);
				So->setH(FPDB15heuristic->calculateHeuristic(board) + SPDB15heuristic->calculateHeuristic(board) + TPDB15heuristic->calculateHeuristic(board));
			}

			So->setG(0);
			start = 0.0;
			finish = 0.0;

			if (!gettimeofday(&t_p,NULL))
				start = (double) t_p.tv_sec + ((double) t_p.tv_usec)/1000000.0;
			else
			{
				cout << "\nProblemas generando el tiempo final.\n";
				exit(EXIT_FAILURE);
			}

			AInstance.run(So);

			if (!gettimeofday(&t_p,NULL))
				finish = (double) t_p.tv_sec + ((double) t_p.tv_usec)/1000000.0;
			else
			{
				cout << "\nProblemas generando el tiempo final.\n";
				exit(EXIT_FAILURE);
			}

			cout << " | Tiempo: " << (finish - start) << endl;


		}

		if(strcmp(heuristic, "Manhattan") == 0)
		{
			delete(M15heuristic);
		}
		else if(strcmp(heuristic, "PDB") == 0)
		{
			delete(TPDB15heuristic);

		}
		file.close();
	}

}

void runFifteenPuzzleNodeIDA(char *filename, char *heuristic)
{
	ifstream file (filename);

	FifteenPuzzleNodeIDA So;
	Manhattan15Puzzle *M15heuristic = NULL;
	PDBHeuristic *FPDB15heuristic = NULL;
	PDBHeuristic *SPDB15heuristic = NULL;
	PDBHeuristic *TPDB15heuristic = NULL;
	IDA IDAInstance;
	int heuristicN = -1, count = 0;
	double start, finish;
	struct timeval t_p;

	int board[16];

	So.g = 0;

	if(strcmp(heuristic, "Manhattan") == 0)
	{
		heuristicN = Manhattan;
		M15heuristic = new Manhattan15Puzzle();
		IDAInstance.manhattanCalculator = M15heuristic;
	}
	else if(strcmp(heuristic, "PDB") == 0)
	{
		heuristicN = PDB;

		int fn = 8, sn = 8, tn = 2;

		bool *fX = new bool[16];
		bool *sX = new bool[16];
		bool *tX = new bool[16];

		fX[0] = false; fX[1] = true; fX[2] = false; fX[3] = true;
		fX[4] = true; fX[5] = false; fX[6] = true; fX[7] = false;
		fX[8] = false; fX[9] = true; fX[10] = false; fX[11] = true;
		fX[12] = true; fX[13] = false; fX[14] = true; fX[15] = false;

		char filename[3] = "f1";

		FPDB15heuristic = new PDBHeuristic(fn, fX, filename);

		IDAInstance.fpdb = FPDB15heuristic;

		sX[0] = false; sX[1] = false; sX[2] = true; sX[3] = false;
		sX[4] = false; sX[5] = true; sX[6] = false; sX[7] = true;
		sX[8] = true; sX[9] = false; sX[10] = true; sX[11] = true;
		sX[12] = false; sX[13] = true; sX[14] = false; sX[15] = true;

		filename[1] = '2';

		SPDB15heuristic = new PDBHeuristic(sn, sX, filename);

		IDAInstance.spdb = SPDB15heuristic;

		tX[0] = false; tX[1] = true; tX[2] = true; tX[3] = true;
		tX[4] = true; tX[5] = true; tX[6] = true; tX[7] = true;
		tX[8] = true; tX[9] = true; tX[10] = true; tX[11] = false;
		tX[12] = true; tX[13] = true; tX[14] = true; tX[15] = true;

		filename[1] = '3';

		TPDB15heuristic = new PDBHeuristic(tn, tX, filename);

		IDAInstance.tpdb = TPDB15heuristic;


	}
	else
	{
		cout << "Heuristica invalida." << endl;
		exit(-1);
	}

	IDAInstance.heuristic  = heuristicN;

	if (file.is_open())
	{
		while ( file.good())
		{
			cout << "Instancia " << count  << ": ";

			for(int i = 0; i < 16; i++)
			{

				file >> board[i];

				So.invBoard[board[i]] = i;

				if(file.eof())
				{
					file.close();
					exit(0);
				}

				cout << board[i] << " ";
			}

			cout << " | ";

			count++;

			if(heuristicN == Manhattan)
				So.h = M15heuristic->initiate(board);
			else
				So.h = FPDB15heuristic->calculateHeuristic(So.invBoard) + SPDB15heuristic->calculateHeuristic(So.invBoard) + TPDB15heuristic->calculateHeuristic(So.invBoard);

			IDAInstance.GlobalNode = So;

			start = 0.0;
			finish = 0.0;

			if (!gettimeofday(&t_p,NULL))
				start = (double) t_p.tv_sec + ((double) t_p.tv_usec)/1000000.0;
			else
			{
				cout << "\nProblemas generando el tiempo final.\n";
				exit(EXIT_FAILURE);
			}

			IDAInstance.Driver();

			if (!gettimeofday(&t_p,NULL))
				finish = (double) t_p.tv_sec + ((double) t_p.tv_usec)/1000000.0;
			else
			{
				cout << "\nProblemas generando el tiempo final.\n";
				exit(EXIT_FAILURE);
			}

			cout << " | Tiempo: " << (finish - start) << endl;


		}
		file.close();
	}

}

int main(int argc, char* argv[])
{

	if(argc == 2 && strcmp(argv[1], "GEN") == 0)
	{

		int fn = 8, sn = 8, tn = 2;

		bool *fX = new bool[16];
		bool *sX = new bool[16];
		bool *tX = new bool[16];

		fX[0] = false; fX[1] = true; fX[2] = false; fX[3] = true;
		fX[4] = true; fX[5] = false; fX[6] = true; fX[7] = false;
		fX[8] = false; fX[9] = true; fX[10] = false; fX[11] = true;
		fX[12] = true; fX[13] = false; fX[14] = true; fX[15] = false;

		char filename[3] = "f1";

		printArray(filename, fX, fn);
		cout << "Listo primer archivo." << endl;

		sX[0] = false; sX[1] = false; sX[2] = true; sX[3] = false;
		sX[4] = false; sX[5] = true; sX[6] = false; sX[7] = true;
		sX[8] = true; sX[9] = false; sX[10] = true; sX[11] = true;
		sX[12] = false; sX[13] = true; sX[14] = false; sX[15] = true;

		filename[1] = '2';

		printArray(filename, sX, sn);
		cout << "Listo segundo archivo." << endl;

		tX[0] = false; tX[1] = true; tX[2] = true; tX[3] = true;
		tX[4] = true; tX[5] = true; tX[6] = true; tX[7] = true;
		tX[8] = true; tX[9] = true; tX[10] = true; tX[11] = false;
		tX[12] = true; tX[13] = true; tX[14] = true; tX[15] = true;

		filename[1] = '3';

		printArray(filename, tX, tn);
		cout << "Listo tercer archivo." << endl;

	}
	else if(argc == 4 && strcmp(argv[2], "IDA*") == 0)
		runFifteenPuzzleNodeIDA(argv[1],argv[3]);
	else if(argc == 4 && strcmp(argv[2], "A*") == 0)
		runFifteenPuzzleNodeA(argv[1],argv[3]);
	else
		cout << "Error en los parametros." << endl;
}
